#include <iostream>
#include <sstream>
#include <cmath>
#include <string>
#include <graph2d.h>
using namespace std;
using namespace graph;

class Fan
{
public:
    Fan();
    Fan(unsigned long c, double r, int vel=SLOW);

    unsigned long getColor() const;
    void setColor(unsigned long c= 0xFF0000);

    bool isOn() const;
    void turnOn();
    void turnOff();

    double getRadius() const;

    int getSpeed() const;
    void setSpeed(int speed=SLOW);
    void draw(double x0, double y0) const;

    string status()const;
    static const int SLOW;
    static const int MEDIUM;
    static const int FAST;
private:
    int speed;
    bool on;
    unsigned long color;
    const double radius;
    double rotX(double x, double y, double theta) const;
    double rotY(double x, double y, double theta) const;
};

const int Fan::SLOW = 1;
const int Fan::MEDIUM = 2;
const int Fan::FAST = 3;

Fan::Fan() : radius(5.0)
{
    speed = SLOW;
    on = false;
    color = 0xFF8800;
}

Fan::Fan(unsigned long c, double r, int vel) : speed(vel), color(c),  radius(r)
{
    on = false;
}

double Fan::rotX(double x, double y, double theta) const
{
     return (x*cos(theta)-y*sin(theta));
}

double Fan::rotY(double x, double y, double theta) const
{
    return (x*sin(theta)+y*cos(theta));
}

void Fan::draw(double x0, double y0) const
{
    double R1=36*radius, R2 = R1/3, R3 = 2*R1/15;
    double L11 = 60*radius, L12 =  46*radius, L21 = 34*radius, H1 = 16*radius, H2 = 80*radius;
    static double dtheta = 0.0;
    double theta0, theta1, theta2;
    const int NFrames = 60;
    string cppstr = status();

    if (on)
    {
        switch (speed)
        {
        case SLOW:
            dtheta += 0.3;
            break;
        case MEDIUM:
            dtheta += 0.5;
            break;
        case FAST:
            dtheta += 0.7;
            break;
        }
        if (dtheta>=2*PI) dtheta = dtheta-2*PI;
    }
    theta0 = dtheta;
    theta1 = 2*PI/3+dtheta;
    theta2 = 4*PI/3+dtheta;

    graph::setColor(BLACK);
    setLineWidth(3);
    triangle(-L21/2+x0, y0+H1, L21/2+x0, y0+H1, x0, y0+H2);

    graph::setColor(color);
    fillTriangle(-L11/2+x0, y0, L11/2+x0, y0, L12/2+x0, y0+H1);
    fillTriangle(L12/2+x0, y0+H1, -L12/2+x0, y0+H1, -L11/2+x0, y0);
    fillTriangle(-L21/2+x0, y0+H1, L21/2+x0, y0+H1, x0, y0+H2);
    fillCircle(x0, H2+y0, R2);
    fillCircle(rotX(R1-R3, 0, theta0)+x0, rotY(R1-R3, 0, theta0)+H2+y0, R3);
    fillCircle(rotX(R1-R3, 0, theta1)+x0, rotY(R1-R3, 0, theta1)+H2+y0, R3);
    fillCircle(rotX(R1-R3, 0, theta2)+x0, rotY(R1-R3, 0, theta2)+H2+y0, R3);
    fillTriangle(x0, H2+y0, rotX(R1-R3, -R3, theta0)+x0, rotY(R1-R3, -R3, theta0)+H2+y0, rotX(R1-R3, R3, theta0)+x0, rotY(R1-R3, R3, theta0)+H2+y0);
    fillTriangle(x0, H2+y0, rotX(R1-R3, -R3, theta1)+x0, rotY(R1-R3, -R3, theta1)+H2+y0, rotX(R1-R3, R3, theta1)+x0, rotY(R1-R3, R3, theta1)+H2+y0);
    fillTriangle(x0, H2+y0, rotX(R1-R3, -R3, theta2)+x0, rotY(R1-R3, -R3, theta2)+H2+y0, rotX(R1-R3, R3, theta2)+x0, rotY(R1-R3, R3, theta2)+H2+y0);
    setLineWidth(2);
    for(int i=0; i<NFrames; i++)
        line(rotX(R2, 0, 2*PI*i/NFrames)+x0, rotY(R2, 0, 2*PI*i/NFrames)+H2+y0, rotX(R1, 0, 2*PI*i/NFrames)+x0, rotY(R1, 0, 2*PI*i/NFrames)+H2+y0);

    graph::setColor(BLACK);
    setLineWidth(3);
    line(-L11/2+x0, y0, L11/2+x0, y0);
    line(L11/2+x0, y0, L12/2+x0, y0+H1);
    line(L12/2+x0, y0+H1, -L12/2+x0, y0+H1);
    line(-L12/2+x0, y0+H1, -L11/2+x0, y0);
    circle(x0, H2+y0, R1);
    circle(x0, H2+y0, R2);

    graph::setColor(WHITE);
    putText(-L11/3+x0, y0+H1/2, cppstr.c_str());
}

unsigned long Fan::getColor() const
{
    return color;
}

void Fan::setColor(const unsigned long c)
{
    color = c;
}

bool Fan::isOn() const
{
    return on;
}

void Fan::turnOn()
{
    on = true;
}

void Fan::turnOff()
{
    on = false;
}

double Fan::getRadius() const
{
    return radius;
}

int Fan::getSpeed() const
{
    return speed;
}

void Fan::setSpeed(int vel)
{
    speed = vel;
}

string Fan::status()const
{
    ostringstream os;

    if(isOn())
    {
        switch (speed)
        {
        case SLOW:
            os << "SLOW ";
            break;
        case MEDIUM:
            os << "MEDIUM ";
            break;
        case FAST:
            os << "FAST ";
            break;
        }
    }
    else
        os << "Fan is OFF: ";

    switch (color)
    {
    case 0xFFFFFF:
        os << "WHITE ";
        break;
    case 0xFF0000:
        os << "RED ";
        break;
    case 0x00FF00:
        os << "GREEN ";
        break;
    case 0x0000FF:
        os << "BLUE ";
        break;
    }

    os << radius;

    return os.str();
}

Fan *g_pFan=NULL;

void display()
{
    g_pFan->draw(getWinWidth()/2, getWinHeight()/12);
}

void keyboard(unsigned char key)
{
    switch (key)
    {
    case '1':
        if (g_pFan!=NULL)
        {
            delete g_pFan;
            g_pFan = NULL;
        }
        g_pFan = new Fan;
        g_pFan->turnOn();
        break;
    case '2':
        if (g_pFan!=NULL)
        {
            delete g_pFan;
            g_pFan = NULL;
        }
        g_pFan = new Fan(0xFF0000, 6);
        g_pFan->turnOn();
        break;
    case '3':
        if (g_pFan!=NULL)
        {
            delete g_pFan;
            g_pFan = NULL;
        }
        g_pFan = new Fan(0x008800, 4);
        g_pFan->turnOn();
        break;
    case '4':
        if (g_pFan!=NULL)
        {
            delete g_pFan;
            g_pFan = NULL;
        }
        g_pFan = new Fan(0x8800FF, 3);
        g_pFan->turnOn();
        break;
    case ' ':
        if (g_pFan->isOn())
            g_pFan->turnOff();
        else
            g_pFan->turnOn();
        break;
    case '.':
        if (g_pFan->getSpeed() != Fan::FAST)
        {
            g_pFan->setSpeed(g_pFan->getSpeed()+1);
        }
        break;
    case ',':
        if (g_pFan->getSpeed() != Fan::SLOW)
        {
            g_pFan->setSpeed(g_pFan->getSpeed()-1);
        }
        break;
    }
}

int main()
{
    g_pFan = new Fan;
    cout << "Fan 1:" << endl;
    cout << g_pFan->status() << endl;
    g_pFan->turnOn();
    g_pFan->setSpeed(Fan::FAST);
    cout << g_pFan->status() << endl;

    initGraph(display, keyboard);

    return 0;
}
